Choropleth Maps

Choropleth Maps

  • Geographic map (U.S., world, continent) that shows how a variable is geographically distributed.

  • R has great packages like tigris (for U.S.) and rnaturalearth (for world maps).

  • Install and load the packages at the top of the R script.

U.S. Maps Using Tigris

  • Start simple: Use tigris to get a national map with state boundaries (take out PR)

  • This code will use tigris to create a data object stored in your environment. Note that adding “shift_geometry” is a key command for putting Alaska and Hawaii on the bottom left.

st <- states(cb = TRUE, resolution = "20m", progress_bar=FALSE) |>
  filter(NAME != "Puerto Rico") |>
  shift_geometry()

U.S. Maps Using Tigris

  • Check the “class” of this data/maps object. You’ll see it’s a “special features” (sf) and a data frame.
    • special features (sf) = contains geographical information for mapping. Check data.
class(st)
[1] "sf"         "data.frame"

Quick Map

ggplot(st) + 
  geom_sf()

Quick Map

  • Remove gray background with “theme_void”:
ggplot(st) + 
  geom_sf() + 
  theme_void()

Quick Map

  • Change fill and border colors and borderline thickness:
ggplot(st) + 
  geom_sf(fill="dodgerblue", color="white", linewidth=.1) + 
  theme_void()

Choropleth Map: Biden v. Trump 2020

  • Import 2020 state election data
v2020 <- read_csv("us_vote_2020.csv")

Data Prep for Choropleth Map

  • We need to merge our election data (v2020) with our “sf” maps object, “st”
  • See Ch. 3 in ModernDive, section 3.7 (using the “join” function)

Data Prep for Choropleth Map

  • First, match state names across datasets; use the “mutate” function, which generates a new variable in the data object.
v2020 <- v2020 |> mutate(NAME=state)
  • This sets up for the merge, which requires you to have one identification variable in common between the two datasets.
  • In this case, that’s going to be “NAME.”

Merge Data Into Maps Object

  • Merge our v2020 data object into our st object containing geographic information using “left_join.”
  • Put the “master” data object first (i.e., st) and the data object that you’re merging into the master object second.
    • Always merge the data object into the simple features (maps) object
  • “left_join” automatically merges by the variable that is common between the two datasets. Again, in this example, that’s “NAME.”
stv20 <- left_join(st, v2020)

Map It!

  • Using “theme_map” (from ggthemes), center title, make a little bigger (expand=FALSE).
    • Check out some of my other specifications that precede this one.
ggplot(stv20, aes(fill = called)) + 
  geom_sf(color = "gray90", linewidth=.2) + 
  scale_fill_manual(values = c("blue", "red"), labels=c("Biden", "Trump")) + 
  theme_map() + coord_sf(expand=FALSE) +
  labs(fill = "",
       title = " 2020 US presidential election results by state",
       caption = "Note: Nebraska and Maine split electoral college votes by congressional district") + 
  theme(plot.title=element_text(face="bold", size=12, hjust=.5), 
        plot.caption = element_text(size=10), 
        legend.text = element_text(size = 8))

Map It!

Counties, Palettes

  • From line 232 in R script:

Counties, Palettes

  • “scale_gradient2” to define midpoint (line 265)

World Maps Using rnaturalearth

  • Bring in world map from rnaturalearth
w <- ne_countries(scale = "medium", returnclass = "sf")

World Maps Using rnaturalearth

w |> 
  ggplot() + 
  geom_sf(aes(fill=income_grp)) + 
  scale_fill_brewer(palette = "YlGnBu")

World Maps Using rnaturalearth

  • Change direction of scale - higher values of income darker
w |> 
  ggplot() + 
  geom_sf(aes(fill=income_grp)) + 
  scale_fill_brewer(palette = "YlGnBu", direction=-1)

World Maps Using rnaturalearth

  • Remove Antarctica and others; remove gray background, relabel legend, theme map
  • Use “Robinson” projection (NYT standard), crs=‘ESRI:54030’
w |> 
  filter(type != "Dependency", type !="Disputed", type != "Indeterminate") |>
  ggplot() + geom_sf(aes(fill=income_grp), color="black", linewidth=.1) + 
  scale_fill_brewer(palette = "YlGnBu", direction=-1, 
                    labels=c("High OECD", "High non-OECD", "Upper middle", "Lower middle", "Low")) +
  coord_sf(crs='ESRI:54030', expand=FALSE) +
  labs(fill = "Income Level", title = "World Map, Country Income Levels",
       caption="Source: R Natural Earth Data") +
  theme_map() +
  theme(plot.title=element_text(face="bold", size=12, hjust=.5), 
        plot.caption = element_text(size=10), 
        legend.title = element_text(size=10),
        legend.text = element_text(size = 8))

World Maps Using rnaturalearth

Merge in Variable for World Choropleth Map

  • Let’s look at the degree of political, rhetorical attacks (by politicians and elites) on the judiciary.
  • Merge in VDem data; I have already created a country id variale (“geounit”) that is common between data objects.
v <- read_dta("vdem.dta")

# Merge vdem with world data
vw <- left_join(w, v)

Generate the Map

  • Let’s look at the degree of political, rhetorical attacks (by politicians and elites) on the judiciary.
  • Merge in VDem data; I have already created a country id variale (“geounit”) that is common between data objects.

Generate the Map

vw |>
  filter(type != "Dependency", type !="Disputed", type != "Indeterminate") |>
  ggplot() + 
  geom_sf(color="black", linewidth=0.1, aes(fill = as.factor(v2jupoatck_ord))) + 
  coord_sf(crs='ESRI:54030', expand=FALSE) +
  scale_fill_brewer(type = "seq", palette = "YlGnBu", na.translate=FALSE,
                    labels=c("Daily/weekly", "Every month", "More than once", "Rare", "None"), 
                    direction = -1) + 
  labs(fill = "Attacks", title = "Government Attacks on the Judiciary, 2021",
       subtitle="Based on VDem's v2jupoatck_ord Variable",
       caption="Source: VDem") +
  theme_map() + 
  theme(plot.title=element_text(face="bold", size=15, hjust=.5), 
        plot.subtitle=element_text(size=12, hjust=.5), 
        plot.caption = element_text(size=10), 
        legend.text = element_text(size = 10),
        legend.title = element_text(size = 10) )

Generate the Map